/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */

#include "dextern.h"
#include <sys/param.h>
#include <sys/errno.h>
#include <unistd.h>
#include <locale.h>
#include <stdarg.h>	/* For error() */

static void mktbls(void);
static void others(void);
static void summary(void);
static wchar_t *chcopy(wchar_t *, wchar_t *);
static int setunion(int *, int *);
static void prlook(LOOKSETS *);
static void cpres(void);
static void cpfir(void);
static void cempty(void);
static void stagen(void);
static LOOKSETS *flset(LOOKSETS *);
static void exp_lkst(void);
static void exp_wsets(void);
static void exp_states(void);
static void exp_psmem(void);

	/* lookahead computations */

int TBITSET;
static int tbitset;	/* size of lookahead sets */
LOOKSETS *lkst;
static int lsetsize;

static int nlset = 0;	/* next lookahead set index */
int nolook = 0;		/* flag to suppress lookahead computations */
static LOOKSETS clset;  /* temporary storage for lookahead computations */

static ITEM *psmem, *zzmemsz;
static int new_pstsize = PSTSIZE;

	/* working set computations */

WSET *wsets;
int cwp;
static int wsetsz = 0;		/* number of WSET items in wsets block */

	/* state information */

int nstate = 0;			/* number of states */
static int nstatesz = NSTATES;	/* number of state space allocated	*/
ITEM **pstate;			/* ptr to descriptions of the states	*/
int *tystate;			/* contains type info about the states	*/
int *indgo;			/* index to the stored goto table	*/
static int *tmp_lset;
static int *tstates;		/* states generated by terminal gotos	*/
static int *ntstates;		/* states generated by non-term gotos	*/
static int *mstates;		/* chain of overflows of term/nonterm	*/
				/* generation lists			*/

	/* storage for the actions in the parser */

int *amem, *memp;		/* next free action table position */
int new_actsize = ACTSIZE;

	/* other storage areas */

int *temp1;		/* temp storate, indexed by terms+ntokens or states */
int lineno = 0;		/* current input line number */
int size;
static int fatfl = 1;	/* if on, error is fatal */
static int nerrors = 0;	/* number of errors */

	/* storage for information about the nonterminals */

static int ***pres;		/* vector of pointers to productions	*/
				/* yielding each nonterminal		*/
static LOOKSETS **pfirst;	/* vector of pointers to first sets for	*/
				/* each nonterminal			*/
static int *pempty;		/* vector of nonterminals nontrivially	*/
				/* deriving e				*/
extern int nprodsz;

int
main(int argc, char *argv[])
{
	(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
#define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
#endif
	(void) textdomain(TEXT_DOMAIN);

	setup(argc, argv);		/* initialize and read productions */
	TBITSET = NWORDS(ntoksz*LKFACTOR);
	tbitset = NWORDS(ntokens*LKFACTOR);
	mktbls();
	cpres();	/* make table of which productions yield a */
			/* given nonterminal */
	cempty();	/* make a table of which nonterminals can match	*/
			/* the empty string */
	cpfir();	/* make a table of firsts of nonterminals */
	stagen();	/* generate the states	*/
	output();	/* write the states and the tables */
	go2out();
	hideprod();
	summary();
	callopt();
	others();
	return (0);
}


static void
mktbls(void)
{
	int i;

	size = ntoksz + nnontersz +1;
	if (size < nstatesz)
		size = nstatesz;
	if (size < new_memsize)
		size = new_memsize;

	amem = (int *) malloc(sizeof (int) * new_actsize);
	psmem = (ITEM *) malloc(sizeof (ITEM) * new_pstsize);
	if ((psmem == NULL) || (amem == NULL))
/*
 * TRANSLATION_NOTE  -- This is a message from yacc.
 *	This message is passed to error() function.
 *	This error happens when yacc could not allocate
 *	initial memory to be used for internal tables.
 *
 *	You may just translate this as:
 *	'Could not allocate internally used memory.'
 */
		error(gettext(
		"couldn't allocate initial table"));
	zzmemsz = psmem;
	memp = amem;

	/*
	 * For lkst
	 */
#define	INIT_LSIZE	nnontersz * LKFACTOR

	tmp_lset = (int *)
	    calloc((size_t)(TBITSET * (INIT_LSIZE+1)), sizeof (int));
	if (tmp_lset == NULL)
/*
 * TRANSLATION_NOTE  -- This is a message from yacc.
 *	This message is passed to error() function.
 *	Yacc could not allocate memory for table named lookset.
 *	Do not translate 'lookset'.
 *
 *	You may just translate this as:
 *	'Could not allocate internally used memory.'
 */
		error(gettext(
		"could not allocate lookset array"));
	lkst = (LOOKSETS *) malloc(sizeof (LOOKSETS) * (INIT_LSIZE + 1));
	for (i = 0; i <= INIT_LSIZE; ++i)
		lkst[i].lset = tmp_lset + TBITSET * i;
	tmp_lset = NULL;

	/*
	 * For wsets
	 */
	tmp_lset = (int *)
	    calloc((size_t)(TBITSET * (nnontersz+1)), sizeof (int));
	if (tmp_lset == NULL)
		error(gettext(
		"could not allocate lookset array"));
	wsets = (WSET *) malloc(sizeof (WSET) * (nnontersz + 1));
	for (i = 0; i <= nnontersz; ++i)
		wsets[i].ws.lset = tmp_lset + TBITSET * i;
	tmp_lset = NULL;

	clset.lset = (int *)malloc(sizeof (int)*TBITSET);
	tstates = (int *)malloc(sizeof (int)*(ntoksz + 1));
	ntstates = (int *)malloc(sizeof (int)*(nnontersz + 1));
	temp1 = (int *)malloc(sizeof (int)*size);
	pres = (int ***)malloc(sizeof (int **)*(nnontersz + 2));
	pfirst = (LOOKSETS **)malloc(sizeof (LOOKSETS *)*(nnontersz + 2));
	pempty = (int *)malloc(sizeof (int)*(nnontersz + 1));

	pstate = (ITEM **)malloc(sizeof (ITEM *)*(nstatesz+2));
	tystate = (int *)malloc(sizeof (int)*nstatesz);
	indgo = (int *)malloc(sizeof (int)*nstatesz);
	mstates = (int *)malloc(sizeof (int)*nstatesz);
	defact = (int *)malloc(sizeof (int)*nstatesz);

	if ((lkst == NULL) || (wsets == NULL) || (tstates == NULL) ||
	    (ntstates == NULL) || (temp1 == NULL) || (pres == NULL) ||
	    (pfirst == NULL) || (pempty == NULL) || (pstate == NULL) ||
	    (tystate == NULL) || (indgo == NULL) || (mstates == NULL) ||
	    (defact == NULL) || (clset.lset == NULL))
/*
 * TRANSLATION_NOTE  -- This is a message from yacc.
 *	This message is passed to error() function.
 *	Do not translate mktbls(). It is a function name.
 *
 *	You may just translate this as:
 *	'Could not allocate internally used memory.'
 */
			error(gettext(
			"cannot allocate tables in mktbls()"));

	aryfil(ntstates, nnontersz+1, 0);
	aryfil(tstates, ntoksz+1, 0);
	wsetsz = nnontersz + 1;
	lsetsize = INIT_LSIZE + 1;
}

/* put out other arrays, copy the parsers */
static void
others(void)
{
	extern int gen_lines;
	int c, i, j;
	int tmpline;

	finput = fopen(parser, "r");
	if (finput == NULL)
/*
 * TRANSLATION_NOTE  -- This is a message from yacc.
 *	This message is passed to error() function.
 *	This error message is issued when yacc can not find
 *	the parser to be copied.
 */
		error(gettext(
		"cannot find parser %s"),
		    parser);

	warray(L"yyr1", levprd, nprod);

	aryfil(temp1, nprod, 0);
					/* had_act[i] is either 1 or 0 */
	PLOOP(1, i)
		temp1[i] = ((prdptr[i+1] - prdptr[i]-2) << 1) | had_act[i];
	warray(L"yyr2", temp1, nprod);

	aryfil(temp1, nstate, -10000000);
	TLOOP(i)
		for (j = tstates[i]; j != 0; j = mstates[j])
			temp1[j] = tokset[i].value;
	NTLOOP(i)
		for (j = ntstates[i]; j != 0; j = mstates[j])
			temp1[j] = -i;
	warray(L"yychk", temp1, nstate);

	warray(L"yydef", defact, nstate);

	if ((fdebug = fopen(DEBUGNAME, "r")) == NULL)
		error("cannot open yacc.debug");
	while ((c = getwc(fdebug)) != EOF)
		(void) putwc(c, ftable);
	(void) fclose(fdebug);
	ZAPFILE(DEBUGNAME);

	if (gen_lines)
		(void) fprintf(ftable, "# line\t1 \"%s\"\n", parser);
	tmpline = 1;
	/* copy parser text */
	while ((c = getwc(finput)) != EOF) {
		if (c == '\n')
			tmpline++;
		if (c == L'$') {
			if ((c = getwc(finput)) != L'A')
				(void) putwc(L'$', ftable);
			else { /* copy actions */
				tmpline++;
				faction = fopen(ACTNAME, "r");
				if (faction == NULL)
/*
 * TRANSLATION_NOTE  -- This is a message from yacc.
 *	This message is passed to error() function.
 *	This error is issued when yacc can not open a
 *	temporary file to be used. You do not need to
 *	use the word 'tempfile'. You can translate it to
 *	mean 'temporary file'.
 */
					error(gettext(
					"cannot open action tempfile"));
				while ((c = getwc(faction)) != EOF)
					(void) putwc(c, ftable);
				(void) fclose(faction);
				if (gen_lines)
					(void) fprintf(ftable,
					    "\n# line\t%d \"%s\"",
					    tmpline,
					    parser);
				ZAPFILE(ACTNAME);
				c = getwc(finput);
			}
		}
		(void) putwc(c, ftable);
	}
	(void) fclose(ftable);
}


/* copies string q into p, returning next free char ptr */
static wchar_t *
chcopy(wchar_t *p, wchar_t *q)
{
	while ((*p = *q++) != L'\0')
		++p;
	return (p);
}

#define	ISIZE 400
/* creates output string for item pointed to by pp */
wchar_t *
writem(int *pp)
{
	int i, *p;
	static int isize = ISIZE;
	static wchar_t *sarr = NULL;
	wchar_t *q;

	if (sarr == NULL) {
		sarr = (wchar_t *)malloc(sizeof (wchar_t) * isize);
		if (sarr == NULL)
/*
 * TRANSLATION_NOTE  -- This is a message from yacc.
 *	This message is passed to error() function.
 *	This error is issued when yacc could not allocate
 *	memory for internally used array.
 *
 *	You may just translate this as:
 *	'Could not allocate internally used memory.'
 */
			error(gettext(
			"could not allocate output string array"));
		for (i = 0; i < isize; ++i)
			sarr[i] = L' ';
	}
	for (p = pp; *p > 0; ++p) /* NULL */
		;
	p = prdptr[-*p];
	q = chcopy(sarr, nontrst[*p-NTBASE].name);
	q = chcopy(q, L" : ");

	for (;;) {
		*q++ = ++p == pp ? L'_' : L' ';
		*q = 0;
		if ((i = *p) <= 0)
			break;
		q = chcopy(q, symnam(i));
		while (q > &sarr[isize-30]) {
			static wchar_t *sarrbase;

			sarrbase = sarr;
			isize += ISIZE;
			sarr = (wchar_t *)
			    realloc((char *)sarr, sizeof (*sarr) * isize);
			if (sarr == NULL)
/*
 * TRANSLATION_NOTE  -- This is a message from yacc.
 *	This message is passed to error() function.
 *	This error is issued when yacc could not allocate
 *	memory for internally used array.
 *
 *	You may just translate this as:
 *	'Could not allocate internally used memory.'
 */
				error(gettext(
				    "cannot expand sarr arrays"));
			q = q - sarrbase + sarr;
		}
	}

	/* an item calling for a reduction */
	if ((i = *pp) < 0) {
		q = chcopy(q, L"    (");
		(void) wsprintf(q, "%d)", -i);
	}
	return (sarr);
}

/* return a pointer to the name of symbol i */
wchar_t *
symnam(int i)
{
	wchar_t *cp;

	cp = (i >= NTBASE) ? nontrst[i-NTBASE].name : tokset[i].name;
	if (*cp == L' ')
		++cp;
	return (cp);
}

static int zzcwp = 0;
static int zzclose = 0;
int zzgoent = 0;
int zzgobest = 0;
int zzacent = 0;
int zzexcp = 0;
int zzsrconf = 0;
int zzrrconf = 0;

/* output the summary on the tty */
static void
summary(void)
{
	if (foutput != NULL) {
		(void) fprintf(foutput,
		    "\n%d/%d terminals, %d/%d nonterminals\n",
		    ntokens, ntoksz, nnonter, nnontersz);
		(void) fprintf(foutput,
		    "%d/%d grammar rules, %d/%d states\n",
		    nprod, nprodsz, nstate, nstatesz);
		(void) fprintf(foutput,
		"%d shift/reduce, %d reduce/reduce conflicts reported\n",
		    zzsrconf, zzrrconf);
		(void) fprintf(foutput,
		    "%d/%d working sets used\n", zzcwp, wsetsz);
		(void) fprintf(foutput,
		    "memory: states,etc. %" PRIdPTR
		    "/%d, parser %" PRIdPTR "/%d\n",
		    mem-tracemem, new_memsize,
		    memp-amem, new_actsize);
		(void) fprintf(foutput,
		    "%d/%d distinct lookahead sets\n", nlset, lsetsize);
		(void) fprintf(foutput,
		    "%d extra closures\n", zzclose - 2*nstate);
		(void) fprintf(foutput,
		    "%d shift entries, %d exceptions\n", zzacent, zzexcp);
		(void) fprintf(foutput,
		    "%d goto entries\n", zzgoent);
		(void) fprintf(foutput,
		    "%d entries saved by goto default\n", zzgobest);
	}
	if (zzsrconf != 0 || zzrrconf != 0) {
/*
 * TRANSLATION_NOTE  -- This is a message from yacc.
 *	You may just leave this message un-translated.
 *	This message only makes sense to those who knows
 *	how yacc works, and the person should know what
 *	this message means in English.
 */
		(void) fprintf(stderr, gettext(
		    "\nconflicts: "));
		if (zzsrconf)
			(void) fprintf(stderr, "%d shift/reduce", zzsrconf);
		if (zzsrconf && zzrrconf)
			(void) fprintf(stderr, ", ");
		if (zzrrconf)
			(void) fprintf(stderr, "%d reduce/reduce", zzrrconf);
		(void) fprintf(stderr, "\n");
	}

	if (ftemp != NULL)
		(void) fclose(ftemp);
	if (fdefine != NULL)
		(void) fclose(fdefine);
}

/* write out error comment */
/*PRINTFLIKE1*/
void
error(char *s, ...)
{
	extern char *infile;
	va_list	ap;

	va_start(ap, s);

	++nerrors;
	if (!lineno)
/*
 * TRANSLATION_NOTE  -- This is a message from yacc.
 *	This message is a prefix to the error messages
 *	passed to error() function.
 */
		(void) fprintf(stderr, gettext(
		    "command line: fatal: "));
	else {
		(void) fprintf(stderr, "\"%s\", ", infile);
/*
 * TRANSLATION_NOTE  -- This is a message from yacc.
 *	This message is a prefix to the error messages
 *	passed to error() function.
 */
		(void) fprintf(stderr, gettext(
		    "line %d: fatal: "),
		    lineno);
	}
	(void) vfprintf(stderr, s, ap);
	(void) fprintf(stderr, "\n");
	va_end(ap);
	if (!fatfl)
		return;
	summary();
	exit(1);
}

/*
 * Print out a warning message.
 */
/*PRINTFLIKE2*/
void
warning(int flag, char *s, ...)
{
	extern char *infile;
	va_list	ap;
	va_start(ap, s);

	(void) fprintf(stderr, "\"%s\", ", infile);
	/*
	 * If flag, print lineno as well.
	 */
	if (flag == 0)
/*
 * TRANSLATION_NOTE  -- This is a message from yacc.
 *	This message is a prefix to the warning messages
 *	passed to warning() function.
 */
		(void) fprintf(stderr, gettext(
		    "warning: "));
	else
/*
 * TRANSLATION_NOTE  -- This is a message from yacc.
 *	This message is a prefix to the warning messages
 *	passed to warning() function.
 */
		(void) fprintf(stderr, gettext(
		    "line %d: warning: "),
		    lineno);
	(void) vfprintf(stderr, s, ap);
	(void) fprintf(stderr, "\n");
	va_end(ap);
}

/* set elements 0 through n-1 to c */
void
aryfil(int *v, int n, int c)
{
	int i;
	for (i = 0; i < n; ++i)
		v[i] = c;
}

/* set a to the union of a and b */
/* return 1 if b is not a subset of a, 0 otherwise */
static int
setunion(int *a, int *b)
{
	int i, x, sub;

	sub = 0;
	SETLOOP(i) {
		*a = (x = *a) | *b++;
		if (*a++ != x)
			sub = 1;
	}
	return (sub);
}

static void
prlook(LOOKSETS *p)
{
	int j, *pp;
	pp = p->lset;
	if (pp == 0)
		(void) fprintf(foutput, "\tNULL");
	else {
		(void) fprintf(foutput, " { ");
		TLOOP(j) {
			if (BIT(pp, j))
				(void) fprintf(foutput, "%ws ", symnam(j));
		}
		(void) fprintf(foutput,  "}");
	}
}

/*
 * compute an array with the beginnings of  productions yielding
 * given nonterminals
 * The array pres points to these lists
 * the array pyield has the lists: the total size is only NPROD+1
 */
static void
cpres(void)
{
	int **ptrpy;
	int **pyield;
	int c, j, i;

	/*
	 * 2/29/88 -
	 * nprodsz is the size of the tables describing the productions.
	 * Normally this will be NPROD unless the production tables have
	 * been expanded, in which case the tables will be NPROD * N(where
	 * N is the number of times the tables had to be expanded.)
	 */
	if ((pyield = (int **) malloc(sizeof (int *) * nprodsz)) == NULL)
/*
 * TRANSLATION_NOTE  -- This is a message from yacc.
 *	This message is passed to error() function.
 *	This error is issued when yacc could not allocate
 *	memory for internally used array.
 *
 *	pyield is name of an array. You should not try to translate
 *	this word.
 *
 *	You may just translate this as:
 *	'Could not allocate internally used memory.'
 */
		error(gettext(
		"cannot allocate space for pyield array"));

	ptrpy = pyield;

	NTLOOP(i) {
		c = i+NTBASE;
		pres[i] = ptrpy;
		fatfl = 0;  /* make undefined  symbols  nonfatal */
		PLOOP(0, j) {
			if (*prdptr[j] == c)	/* linear search for all c's */
				*ptrpy++ =  prdptr[j] + 1;
		}
		if (pres[i] == ptrpy) {		/* c not found */
/*
 * TRANSLATION_NOTE  -- This is a message from yacc.
 *	This message is passed to error() function.
 *	Ask somebody who knows yacc how to translate nonterminal or
 *	look at translated yacc document.
 */
			error(gettext(
			"undefined nonterminal: %ws"),
			    nontrst[i].name);
		}
	}
	pres[i] = ptrpy;
	fatfl = 1;
	if (nerrors) {
		summary();
		exit(1);
	}
	if (ptrpy != &pyield[nprod])
/*
 * TRANSLATION_NOTE  -- This is a message from yacc.
 *	This message is passed to error() function.
 *	This is an internal error message.
 *	Very little use to user. You may leave it
 *	un-translated.
 *
 *	pyied is name of an array. Do not translate it.
 */
		error(gettext(
		"internal Yacc error: pyield %d"),
		    ptrpy-&pyield[nprod]);
}

static int indebug = 0;
/* compute an array with the first of nonterminals */
static void
cpfir(void)
{
	int *p, **s, i, **t, ch, changes;

	zzcwp = nnonter;
	NTLOOP(i) {
		aryfil(wsets[i].ws.lset, tbitset, 0);
		t = pres[i+1];
		/* initially fill the sets */
		for (s = pres[i]; s < t; ++s) {
			/* check if ch is non-terminal */
			for (p = *s; (ch = *p) > 0; ++p) {
				if (ch < NTBASE) {	/* should be token */
					SETBIT(wsets[i].ws.lset, ch);
					break;
				} else if (!pempty[ch-NTBASE])
					break;
			}
		}
	}

	/* now, reflect transitivity */

	changes = 1;
	while (changes) {
		changes = 0;
		NTLOOP(i) {
			t = pres[i+1];
			for (s = pres[i]; s < t; ++s) {
				for (p = *s; (ch = (*p-NTBASE)) >= 0; ++p) {
					changes |= setunion(wsets[i].ws.lset,
					    wsets[ch].ws.lset);
					if (!pempty[ch])
						break;
				}
			}
		}
	}

	NTLOOP(i)
		pfirst[i] = flset(&wsets[i].ws);
	if (!indebug)
		return;
	if ((foutput != NULL)) {
		NTLOOP(i) {
			(void) fprintf(foutput, "\n%ws: ", nontrst[i].name);
			prlook(pfirst[i]);
			(void) fprintf(foutput, " %d\n", pempty[i]);
		}
	}
}

/* sorts last state,and sees if it equals earlier ones. returns state number */
int
state(int c)
{
	int size1, size2;
	int i;
	ITEM *p1, *p2, *k, *l, *q1, *q2;
	p1 = pstate[nstate];
	p2 = pstate[nstate+1];
	if (p1 == p2)
		return (0); /* null state */
	/* sort the items */
	for (k = p2 - 1; k > p1; k--) {	/* make k the biggest */
		for (l = k-1; l >= p1; --l)
			if (l->pitem > k->pitem) {
				int *s;
				LOOKSETS *ss;
				s = k->pitem;
				k->pitem = l->pitem;
				l->pitem = s;
				ss = k->look;
				k->look = l->look;
				l->look = ss;
			}
	}
	size1 = p2 - p1; /* size of state */

	for (i = (c >= NTBASE) ? ntstates[c-NTBASE] : tstates[c];
	    i != 0; i = mstates[i]) {
		/* get ith state */
		q1 = pstate[i];
		q2 = pstate[i+1];
		size2 = q2 - q1;
		if (size1 != size2)
			continue;
		k = p1;
		for (l = q1; l < q2; l++) {
			if (l->pitem != k->pitem)
				break;
			++k;
		}
		if (l != q2)
			continue;
		/* found it */
		pstate[nstate+1] = pstate[nstate]; /* delete last state */
		/* fix up lookaheads */
		if (nolook)
			return (i);
		for (l = q1, k = p1; l < q2; ++l, ++k) {
			int s;
			SETLOOP(s)
				clset.lset[s] = l->look->lset[s];
			if (setunion(clset.lset, k->look->lset)) {
				tystate[i] = MUSTDO;
				/* register the new set */
				l->look = flset(&clset);
			}
		}
		return (i);
	}
	/* state is new */
	if (nolook)
/*
 * TRANSLATION_NOTE  -- This is a message from yacc.
 *	This message is passed to error() function.
 *	You may leave this untranslated. Leave
 *	state/nolook un-translated.
 */
		error(gettext(
		"yacc state/nolook error"));
	pstate[nstate+2] = p2;
	if (nstate+1 >= nstatesz)
		exp_states();
	if (c >= NTBASE) {
		mstates[nstate] = ntstates[c - NTBASE];
		ntstates[c - NTBASE] = nstate;
	} else {
		mstates[nstate] = tstates[c];
		tstates[c] = nstate;
	}
	tystate[nstate] = MUSTDO;
	return (nstate++);
}

static int pidebug = 0;

void
putitem(int *ptr, LOOKSETS *lptr)
{
	register ITEM *j;

	if (pidebug && (foutput != NULL))
		(void) fprintf(foutput,
		    "putitem(%ws), state %d\n", writem(ptr), nstate);
	j = pstate[nstate+1];
	j->pitem = ptr;
	if (!nolook)
		j->look = flset(lptr);
	pstate[nstate+1] = ++j;
	if (j > zzmemsz) {
		zzmemsz = j;
		if (zzmemsz >=  &psmem[new_pstsize])
			exp_psmem();
			/* error("out of state space"); */
	}
}

/*
 * mark nonterminals which derive the empty string
 * also, look for nonterminals which don't derive any token strings
 */
static void
cempty(void)
{
#define	EMPTY 1
#define	WHOKNOWS 0
#define	OK 1
	int i, *p;

	/*
	 * first, use the array pempty to detect productions
	 * that can never be reduced
	 */

	/* set pempty to WHONOWS */
	aryfil(pempty, nnonter+1, WHOKNOWS);

	/*
	 * now, look at productions, marking nonterminals which
	 * derive something
	 */
	more:
	PLOOP(0, i) {
		if (pempty[*prdptr[i] - NTBASE])
			continue;
		for (p = prdptr[i] + 1; *p >= 0; ++p)
			if (*p >= NTBASE && pempty[*p-NTBASE] == WHOKNOWS)
				break;
		if (*p < 0) { /* production can be derived */
			pempty[*prdptr[i]-NTBASE] = OK;
			goto more;
		}
	}

	/* now, look at the nonterminals, to see if they are all OK */

	NTLOOP(i) {
		/*
		 * the added production rises or falls as the
		 * start symbol ...
		 */
		if (i == 0)
			continue;
		if (pempty[i] != OK) {
			fatfl = 0;
/*
 * TRANSLATION_NOTE  -- This is a message from yacc.
 *	This message is passed to error() function.
 *	Ask somebody who knows yacc how to translate nonterminal or
 *	look at translated yacc document. Check how 'derive' is
 *	translated in these documents also.
 */
			error(gettext(
			"nonterminal %ws never derives any token string"),
			    nontrst[i].name);
		}
	}

	if (nerrors) {
		summary();
		exit(1);
	}

	/*
	 * now, compute the pempty array, to see which nonterminals
	 * derive the empty string
	 */

	/* set pempty to WHOKNOWS */

	aryfil(pempty, nnonter+1, WHOKNOWS);

	/* loop as long as we keep finding empty nonterminals */

again:
	PLOOP(1, i) {
		/* not known to be empty */
		if (pempty[*prdptr[i]-NTBASE] == WHOKNOWS) {
			for (p = prdptr[i]+1;
			    *p >= NTBASE && pempty[*p-NTBASE] == EMPTY; ++p)
				;
			/* we have a nontrivially empty nonterminal */
			if (*p < 0) {
				pempty[*prdptr[i]-NTBASE] = EMPTY;
				goto again; /* got one ... try for another */
			}
		}
	}
}

/* generate the states */
static int gsdebug = 0;
static void
stagen(void)
{
	int i, j;
	int c;
	register WSET *p, *q;

	/* initialize */

	nstate = 0;

	pstate[0] = pstate[1] = psmem;
	aryfil(clset.lset, tbitset, 0);
	putitem(prdptr[0] + 1, &clset);
	tystate[0] = MUSTDO;
	nstate = 1;
	pstate[2] = pstate[1];

	aryfil(amem, new_actsize, 0);

	/* now, the main state generation loop */

	more:
	SLOOP(i) {
		if (tystate[i] != MUSTDO)
			continue;
		tystate[i] = DONE;
		aryfil(temp1, nnonter + 1, 0);
		/* take state i, close it, and do gotos */
		closure(i);
		WSLOOP(wsets, p) { /* generate goto's */
			if (p->flag)
				continue;
			p->flag = 1;
			c = *(p->pitem);
			if (c <= 1) {
				if (pstate[i+1]-pstate[i] <= p-wsets)
					tystate[i] = MUSTLOOKAHEAD;
				continue;
			}
			/* do a goto on c */
			WSLOOP(p, q) {
				/* this item contributes to the goto */
				if (c == *(q->pitem)) {
					putitem(q->pitem + 1, &q->ws);
					q->flag = 1;
				}
			}
			if (c < NTBASE)
				(void) state(c);  /* register new state */
			else temp1[c-NTBASE] = state(c);
		}
		if (gsdebug && (foutput != NULL)) {
			(void) fprintf(foutput,  "%d: ", i);
			NTLOOP(j) {
				if (temp1[j])
					(void) fprintf(foutput,
					    "%ws %d, ", nontrst[j].name,
					    temp1[j]);
			}
			(void) fprintf(foutput, "\n");
		}
		indgo[i] = apack(&temp1[1], nnonter - 1) - 1;
		goto more; /* we have done one goto; do some more */
	}
	/* no more to do... stop */
}

/* generate the closure of state i */
static int cldebug = 0; /* debugging flag for closure */

void
closure(int i)
{
	int c, ch, work, k;
	register WSET *u, *v;
	int *pi;
	int **s, **t;
	ITEM *q;
	register ITEM *p;
	int idx1 = 0;

	++zzclose;

	/* first, copy kernel of state i to wsets */
	cwp = 0;
	ITMLOOP(i, p, q) {
		wsets[cwp].pitem = p->pitem;
		wsets[cwp].flag = 1;    /* this item must get closed */
		SETLOOP(k)
			wsets[cwp].ws.lset[k] = p->look->lset[k];
		WSBUMP(cwp);
	}

	/* now, go through the loop, closing each item */

	work = 1;
	while (work) {
		work = 0;
		/*
		 * WSLOOP(wsets, u) {
		 */
		for (idx1 = 0; idx1 < cwp; idx1++) {
			u = &wsets[idx1];
			if (u->flag == 0)
				continue;
			c = *(u->pitem);  /* dot is before c */
			if (c < NTBASE) {
				u->flag = 0;
				/*
				 * only interesting case is where . is
				 * before nonterminal
				 */
				continue;
			}

			/* compute the lookahead */
			aryfil(clset.lset, tbitset, 0);

			/* find items involving c */

			WSLOOP(u, v) {
				if (v->flag == 1 && *(pi = v->pitem) == c) {
					v->flag = 0;
					if (nolook)
						continue;
					while ((ch = *++pi) > 0) {
						/* terminal symbol */
						if (ch < NTBASE) {
							SETBIT(clset.lset, ch);
							break;
						}
						/* nonterminal symbol */
						(void) setunion(clset.lset,
						    pfirst[ch-NTBASE]->lset);
						if (!pempty[ch-NTBASE])
							break;
					}
					if (ch <= 0)
						(void) setunion(clset.lset,
						    v->ws.lset);
				}
			}

			/*  now loop over productions derived from c */

			c -= NTBASE; /* c is now nonterminal number */

			t = pres[c+1];
			for (s = pres[c]; s < t; ++s) {
				/* put these items into the closure */
				WSLOOP(wsets, v) { /* is the item there */
					/* yes, it is there */
					if (v->pitem == *s) {
						if (nolook)
							goto nexts;
						if (setunion(v->ws.lset,
						    clset.lset))
							v->flag = work = 1;
						goto nexts;
					}
				}

				/*  not there; make a new entry */
				if (cwp + 1 >= wsetsz)
					exp_wsets();

				wsets[cwp].pitem = *s;
				wsets[cwp].flag = 1;
				if (!nolook) {
					work = 1;
					SETLOOP(k)
						wsets[cwp].ws.lset[k] =
						    clset.lset[k];
				}
				WSBUMP(cwp);
				nexts:;
			}
		}
	}

	/* have computed closure; flags are reset; return */

	if (&wsets[cwp] > &wsets[zzcwp])
		zzcwp = cwp;
	if (cldebug && (foutput != NULL)) {
		(void) fprintf(foutput, "\nState %d, nolook = %d\n", i, nolook);
		WSLOOP(wsets, u) {
			if (u->flag)
				(void) fprintf(foutput, "flag set!\n");
			u->flag = 0;
			(void) fprintf(foutput, "\t%ws", writem(u->pitem));
			prlook(&u->ws);
			(void) fprintf(foutput,  "\n");
		}
	}
}

static LOOKSETS *
flset(LOOKSETS *p)
{
	/* decide if the lookahead set pointed to by p is known */
	/* return pointer to a perminent location for the set */

	int j, *w;
	int *u, *v;
	register LOOKSETS *q;

	for (q = &lkst[nlset]; q-- > lkst; ) {
		u = p->lset;
		v = q->lset;
		w = & v[tbitset];
		while (v < w)
			if (*u++ != *v++)
				goto more;
		/* we have matched */
		return (q);
		more:;
	}
	/* add a new one */
	q = &lkst[nlset++];
	if (nlset >= lsetsize) {
		exp_lkst();
		q = &lkst[nlset++];
	}
	SETLOOP(j) q->lset[j] = p->lset[j];
	return (q);
}

static void
exp_lkst(void)
{
	int i, j;
	static LOOKSETS *lookbase;

	lookbase = lkst;
	lsetsize += LSETSIZE;
	tmp_lset = (int *)
	    calloc((size_t)(TBITSET * (lsetsize-LSETSIZE)), sizeof (int));
	if (tmp_lset == NULL)
/*
 * TRANSLATION_NOTE  -- This is a message from yacc.
 *	This message is passed to error() function.
 *	Memory allocation error. Do not translate lookset.
 *
 *	You may just translate this as:
 *	'Could not allocate internally used memory.'
 */
		error(gettext(
		"could not expand lookset array"));
	lkst = (LOOKSETS *) realloc((char *)lkst, sizeof (LOOKSETS) * lsetsize);
	for (i = lsetsize-LSETSIZE, j = 0; i < lsetsize; ++i, ++j)
		lkst[i].lset = tmp_lset + TBITSET * j;
	tmp_lset = NULL;
	if (lkst == NULL)
/*
 * TRANSLATION_NOTE  -- This is a message from yacc.
 *	This message is passed to error() function.
 *	Memory allocation error. Do not translate lookset.
 *
 *	You may just translate this as:
 *	'Could not allocate internally used memory.'
 */
		error(gettext(
		"could not expand lookahead sets"));
	for (i = 0; i <= nnonter; ++i)
		pfirst[i] = pfirst[i] - lookbase + lkst;
	for (i = 0; i <= nstate+1; ++i) {
		if (psmem[i].look)
			psmem[i].look = psmem[i].look - lookbase + lkst;
		if (pstate[i]->look)
			pstate[i]->look = pstate[i]->look - lookbase + lkst;
	}
}

static void
exp_wsets(void)
{
	int i, j;

	wsetsz += WSETSIZE;
	tmp_lset = (int *)
	    calloc((size_t)(TBITSET * (wsetsz-WSETSIZE)), sizeof (int));
	if (tmp_lset == NULL)
/*
 * TRANSLATION_NOTE  -- This is a message from yacc.
 *	This message is passed to error() function.
 *	Memory allocation error. Do not translate lookset.
 *
 *	You may just translate this as:
 *	'Could not allocate internally used memory.'
 */
		error(gettext(
		"could not expand lookset array"));
	wsets = (WSET *) realloc((char *)wsets, sizeof (WSET) * wsetsz);
	for (i = wsetsz-WSETSIZE, j = 0; i < wsetsz; ++i, ++j)
		wsets[i].ws.lset = tmp_lset + TBITSET * j;
	tmp_lset = NULL;
	if (wsets == NULL)
/*
 * TRANSLATION_NOTE  -- This is a message from yacc.
 *	This message is passed to error() function.
 *	Memory allocation error. You may just transltate
 *	this as 'Could not allocate internally used memory.'
 *
 *	You may just translate this as:
 *	'Could not allocate internally used memory.'
 */
		error(gettext(
		"could not expand working sets"));
}

static void
exp_states(void)
{
	nstatesz += NSTATES;

	pstate = (ITEM **)
	    realloc((char *)pstate, sizeof (ITEM *)*(nstatesz+2));
	mstates = (int *)realloc((char *)mstates, sizeof (int)*nstatesz);
	defact = (int *)realloc((char *)defact, sizeof (int)*nstatesz);
	tystate = (int *)realloc((char *)tystate, sizeof (int)*nstatesz);
	indgo = (int *)realloc((char *)indgo, sizeof (int)*nstatesz);

	if ((*pstate == NULL) || (tystate == NULL) || (defact == NULL) ||
	    (indgo == NULL) || (mstates == NULL))
/*
 * TRANSLATION_NOTE  -- This is a message from yacc.
 *	This message is passed to error() function.
 *	Memory allocation error.
 *
 *	You may just translate this as:
 *	'Could not allocate internally used memory.'
 */
		error(gettext(
		"cannot expand table of states"));
}

static void
exp_psmem(void)
{
	int i;

	new_pstsize += PSTSIZE;
	psmem = (ITEM *) realloc((char *)psmem, sizeof (ITEM) * new_pstsize);
	if (psmem == NULL)
/*
 * TRANSLATION_NOTE  -- This is a message from yacc.
 *	This message is passed to error() function.
 *	Memory allocation error.
 *
 *	You may just translate this as:
 *	'Could not allocate internally used memory.'
 */
		error(gettext(
		"cannot expand pstate memory"));

	zzmemsz = zzmemsz - pstate[0] + psmem;
	for (i = 1; i <= nstate+1; ++i)
		pstate[i] = pstate[i] - pstate[0] + psmem;
	pstate[0] = psmem;
}
